package com.hioove.core.process;

import com.hioove.core.annotation.Bean;
import com.hioove.core.model.base.config.ClassSupport;
import com.hioove.core.model.base.config.ConfigInfor;
import com.hioove.core.model.base.config.ConfigInstance;
import com.hioove.core.reflect.SqlProxy;
import com.hioove.core.util.Builder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * todo
 */
public class Analysis {
    static Logger log = LoggerFactory.getLogger(Analysis.class);

    /**
     * @param fieldType
     * @param tobeObj
     * @param configObj
     * @param mvcObj
     * @return
     */
    // 优先按类型获取 然后按照名称获取 不存在的类尝试实例化
    public static Object getClassByType(Class<?> fieldType, Map<String, ClassSupport> tobeObj, ConfigInstance configObj, Map<Class<?>, Object> mvcObj, HashMap<Class<?>, Object> newInjectedObject) {

        Map<String, Object> beanCalss = Optional.ofNullable((Map<String, Object>) (configObj.getAnnotationInstance().get(Bean.class))).orElseGet(HashMap::new);
        Map<Class<?>, Object> mvcBeanCalss = Optional.ofNullable(mvcObj).orElseGet(HashMap::new);
        Map<String, ClassSupport> toBeBeanCalss = Optional.ofNullable(tobeObj).orElseGet(HashMap<String, ClassSupport>::new);
        // 按类获取已经实例化对象
        Object obj = !Objects.isNull(newInjectedObject.get(fieldType)) ? newInjectedObject.get(fieldType) : Optional.ofNullable(mvcBeanCalss.get(fieldType)).orElse(beanCalss.get(fieldType.getSimpleName()));
        // 保证这个类必须是被scan 要考虑到接口类型需要找到子类
        obj = !Objects.isNull(obj) && !fieldType.isInterface() ? obj :
                toBeBeanCalss.values().stream().filter(classConfig -> {
                    return classConfig.getClazz().equals(fieldType);
                }).map(classConfig -> {
                    try {
                        return classConfig.getClazz().newInstance();
                    } catch (Exception e) {
                        log.error("Failed to construct instantiated object!!!", e);
                    }
                    throw new RuntimeException("Class not loaded!!! " + fieldType.getName());
                }).collect(Collectors.toList()).stream().findFirst().get();
        newInjectedObject.put(fieldType, obj);
        return obj;
    }

    // 优先类名获取类 而后 找子类进行实例化
    public static <T> T getClassByType(Class<T> fieldType, Map<String, ClassSupport> tobeObj, ConfigInstance configObj, HashMap<Object, Object> newInjectedObject) {

        Map<String, Object> beanCalss = Optional.ofNullable((Map<String, Object>) (configObj.getAnnotationInstance().get(Bean.class))).orElseGet(HashMap::new);
        Map<String, ClassSupport> toBeBeanCalss = Optional.ofNullable(tobeObj).orElseGet(HashMap<String, ClassSupport>::new);
        // 按类获取已经实例化对象
        Optional existObj = !Objects.isNull(newInjectedObject.get(fieldType)) ? Optional.ofNullable(newInjectedObject.get(fieldType.getClass())) : Optional.ofNullable(beanCalss.get(fieldType.getSimpleName()));

        Stream<?> stream = toBeBeanCalss.values().stream().filter(classConfig -> {
            return classConfig.getClazz().equals(fieldType);
        }).map(classConfig -> {

            // 加载当前类到jvm
            return classConfig.getSubClazz().stream().map(subclass -> {
                try {
                    return subclass.newInstance();
                } catch (Exception e) {
                    log.error("Failed to construct instantiated object!!!", e);
                }
                return null;
            }).collect(Collectors.toList());
        }).flatMap(Collection::stream);
        // 保证这个类必须是被scan 要考虑到接口类型需要找到子类
        T obj = existObj.isPresent() && fieldType.isInstance(existObj) ? (T) existObj.get() : fieldType.isArray() ? (T) stream.toArray() : (T) stream.collect(Collectors.toList()).stream().findFirst().get();

        newInjectedObject.put(fieldType, obj);
        return obj;

    }

    // 优先类名获取类 而后 找子类进行实例化  主要是mybatis 的实现
    public static <T> T getMapperClassByType(Class<T> fieldType, ConfigInfor configInfo, HashMap<Class<?>, Object> newInjectedObject) {
        // 关于 mybatis 数据库需要实现
        Map<?, ?> sqlSupport = configInfo.getSqlInfo();
        String mapperName = fieldType.getTypeName();
        SqlProxy build = Builder.of(SqlProxy.getInstance()).build();
        T obj = !Objects.isNull(newInjectedObject.get(fieldType)) ? (T) newInjectedObject.get(fieldType) : build.create(fieldType);
        newInjectedObject.put(fieldType, obj);
        return obj;
    }

}

